home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-12-11 | 19.4 KB | 508 lines | [TEXT/PJMM] |
- {*** THIS IS A MODIFICATION OF THE GENERIC applicationProcs.p UNIT TO DO A SIMPLE }
- { GRAPHING PROGRAM USING EXPRESSIONS, SCALEDGRAPHICSDECORATIONS, ETC. *** }
-
- { many of the comments are from the original file, applicationProcs.p }
-
- { NOTE: The def. of type GraphDec and its use in MyWin are the main points here; the only changes to }
- { the generic MyWin type are in the methods for SetDefaults and for OpenInRect. The actual }
- { graphing is done in procedure graphDec.doDraw, as part of the definition of the "decoration" }
- { class graphDec. }
-
-
- unit applicationProcs;
-
- { This is the unit that you must modify to create a new application. This unit }
- { uses object definitions from other units, and it is in turn used by the main }
- { program, StandardMain. However, you don't have to make any changes in the }
- { other units or in the main program. }
-
- { This unit works with StandardMain.p and xWindow.p. It also requires the }
- { presence of certain resources (those in generc.rsrc). There are many other units }
- { in the project as distributed. You can add those you need to the uses clause below. }
-
- interface
-
- uses
- xWindow, expression, xInputDecoration, xExpressionInput, xScaledGraphicsDecoration, xControlDecoration, xFigureDecoration;
-
-
- const
-
- maxSleepTime = 5; { When the main program has no events to process, it "goes to }
- { sleep" and yields processing time to other processes that may be running. }
- { However, this program still wants to process "idle" events. The constant }
- { maxSleepTime specifies the longest time this program is willing to sleep }
- { between idle events. The time is specified in ticks (60 ticks = 1 second), so }
- { a value of 5 corresponds to about 12 idle events per second, which is OK for }
- { most purposes. However, if you are using idle events to drive a processing }
- { task (such as drawing successive pieces of a graph on each idle event), you }
- { can change this value, setting it as low as 0 if you like, in which case the }
- { Mac will give as little time as possible to other processes. }
-
-
- { The constants defined below are used by the main program to set up the "About }
- { Box" that is displayed when the user chooses "About . . . " from the apple menu. }
- { Change them as appropriate for the program you are writing. }
-
- ApplicationShortName = 'Generic Application';
- { This is the name of the program displayed in the first line of the Apple }
- { Menu. In this example, "About Generic Application..." will appear there. }
-
- ApplicationLongName = 'Generic Macintosh Application , version 0.9 ';
- { Provides a fuller description of the program that will be displayed at the }
- { top of the "About Box". }
-
- AuthorName = 'David Eck';
- AuthorAddress1 = 'Hobart and William Smith Colleges';
- AuthorAddress2 = 'Geneva, NY 14456';
- AuthorAddress3 = '(Email Address: eck@hws.BITNET)';
- { These four items are displayed on the next four lines of the About Box, under }
- { the heading "Created By:" You can, of course, leave some of these empty, or }
- { use some of them for information other than an address. }
-
- CommentLine = 'This is a shell program that you can use as a basis for writing Macintosh applications.';
- { This line is simply displayed at the bottom of the about box. }
-
-
-
- var
- editMenu: MenuHandle; { These two variables provide references to the File and }
- fileMenu: MenuHandle; { Edit menus. The main program requires that they be here. }
-
-
- { The following is a list of procedures called by the main program. You should not}
- { remove or change the name or parameter list for any of these procedures. If your }
- { program does not require one of these procedures, just leave its definition blank. }
-
- procedure InitializeApplication;
- { Called when the program is first starting up; use this procedure to initialize }
- { any global variables, open any windows that you want to appear automatically }
- { on the screen at startup, etc. }
-
- procedure CleanUpApplication;
- { This is called just before the program ends, to give you a final chance to clean }
- { up. Most programs will have no use for this. There is no way to abort program }
- { termination from this procedure. }
-
- procedure DoEditMenu (itemNum: integer);
- { The user has chosen the specified item number from the Edit menu. This }
- { procedure should carry out that command. (Items in menus are numbered }
- { from the top down, starting with 1. Separating lines in the menu are numbered}
- { even though they are not really commands.) }
-
- procedure DoFileMenu (itemNum: integer;
- var done: boolean);
- { Perform command number "itemNum" from the File Menu; if the user has chosen }
- { the Quit command, then the parameter "done" should be set to TRUE, unless }
- { the command is canceled in one way or another (for example, if you put up a }
- { dialog box that says "Do you really want to quit?" and the user answers No). }
- { In this sample program, the only commands in the File menu are New (command }
- { #1), Close (#2) and Quit (#4). }
-
- procedure DoOtherMenu (menuNum, itemNum: integer);
- { This procedure is here just in case you add more menus to the menu bar. The }
- { user has selected item number "itemNum" from menu number "menuNum", }
- { and this procedure should carry out that command. Note that the menu number }
- { used here is the menu ID (or resource number), not the position of the menu in }
- { the menu bar. }
-
- procedure UpdateMenus;
- { This procedure is called just BEFORE the user makes a selection from the }
- { menu bar, after the user has clicked in the menu bar, but before any menu is }
- { displayed. Use this procedure to enable and disable items in the menu, to set }
- { the names of items with changeable names, to check and uncheck items, etc. }
-
- procedure ApplicationIdle;
- { This procedure is called periodically, unless the computer is otherwise occupied }
- { (for example, tied up in a lengthy computation). See the description of the constant }
- { maxSleepTime above. }
-
-
-
- implementation
-
-
- type
-
- graphDec = object(xScaledGraphicsWithAxes)
- { decoration containing a set of axes, plus the graph of a function }
- exp: expression;
- procedure SetUp (win: xWindow;
- theLeft, theTop, theWidth, theHeight: integer);
- override;
- procedure doDraw;
- override;
- procedure InstallExpression (theExp: expression);
- end;
-
- calcButton = object(xDefaultButton)
- { a button the user can press to draw a graph }
- procedure HandleClick;
- override;
- end;
-
- myWin = object(xWindow)
-
- { Window contains an area for drawing the graph of a function, a button the user }
- { user can press to draw the graph, and five input boxes where the user enters }
- { the expression that defines the function, and the minimum and maximum values }
- { of x and y for use on the graph. }
-
- graph: graphDec;
- calc: calcButton;
- expIn: xExpressionInput;
- xminIn, xmaxIn, yminIn, ymaxIn: xRealInput;
-
- procedure setDefaults;
- override;
- procedure openInRect (title: string;
- left, top, right, bottom: integer);
- override;
- procedure doRedraw (badRect: Rect);
- override;
- procedure doContentClick (localPt: Point;
- modifiers: longint);
- override;
- procedure doKey (ch: char;
- modifiers: longint);
- override;
-
- end;
-
- var
- xRef: integer; { allows access to the variable "x" used in expressions; this is used }
- { in some of the procedures from the UNIT "expression". }
-
-
- procedure graphDec.SetUp (win: xWindow;
- theLeft, theTop, theWidth, theHeight: integer);
- begin
- inherited Setup(win, theLeft, theTop, theWidth, theHeight);
- exp := nil; { initially, no expression is given }
- end;
-
- procedure graphDec.doDraw;
- var
- deltaX, x, y, oldX, oldY: extended;
- i: integer;
- cases1, temp, cases2: handle;
- begin
- inherited doDraw; { draws the axes }
- if exp <> nil then begin { draw a graph of the expression }
- cases1 := NewHandle(0);
- cases2 := NewHandle(0);
- deltaX := (xmax - xmin) / 100;
- oldX := xmin;
- SetVariableValue(xRef, xmin);
- oldY := exp.valueWithCases(cases1);
- ForeColor(blueColor);
- for i := 1 to 100 do begin
- x := xmin + i * deltaX;
- SetVariableValue(xRef, x);
- y := exp.valueWithCases(cases2);
- { The object of the test in the next IF statement is to avoid drawing a connection line }
- { between successive points in the graph unless we are sure that those points should }
- { be connected. Procedure SameCases, from UNIT expression, tries to check for discontinuities. }
- { NOTE that a more sophisticated way of handling the case where SameCases is false is needed, such }
- { looking at extra points between the two x-values involved. }
- if SameCases(cases1, cases2) & (oldX < infinity) & (oldY < infinity) & (x < infinity) & (y < infinity) then
- DrawLine(oldX, oldY, x, y);
- oldX := x;
- oldY := y;
- temp := cases2;
- cases2 := cases1;
- cases1 := temp;
- end;
- ForeColor(blackColor);
- end;
- end;
-
- procedure graphDec.InstallExpression (theExp: expression);
- { called when there is a new function to be graphed. }
- begin
- if exp <> nil then
- exp.kill;
- exp := theExp;
- forceRedraw;
- end;
-
- procedure calcButton.HandleClick;
- { called when user clicks on button to draw a new graph. The values entered by the }
- { user are retrieved and checked, as is the function definition. If any error is found, }
- { nothing is done. Otherwise, the new graph is drawn. }
- var
- e: expression;
- err: boolean;
- x1, x2, y1, y2: extended;
- begin
- with myWin(itsWindow) do begin
- xminIn.getNumber(x1, err);
- if err then
- EXIT(HandleClick);
- xmaxIn.getNumber(x2, err);
- if err then
- EXIT(HandleClick);
- if x2 <= x1 then begin
- TellUser('The maximum x value you specify must be larger than your minimum x value.');
- EXIT(HandleClick);
- end;
- yminIn.getNumber(y1, err);
- if err then
- EXIT(HandleClick);
- ymaxIn.getNumber(y2, err);
- if err then
- EXIT(HandleClick);
- if y2 <= y1 then begin
- TellUser('The maximum y value you specify must be larger than your minimum y value.');
- EXIT(HandleClick);
- end;
- expIn.GetExpression(e, err);
- if err then
- graph.InstallExpression(nil)
- else begin
- graph.SetCoordinates(x1, x2, y1, y2);
- graph.InstallExpression(e);
- expIn.Hilite(0, 32000);
- end;
- end;
- end;
-
- procedure myWin.setDefaults;
- { This procedure sets up the defalut appearance of the window, and initializes certain }
- { instance variables. It is called BEFORE the window is opened, by openInRect. }
- begin
- inherited setDefaults;
- SetFeatures([hasGrow, hasZoom, hasGoAway]);
- end;
-
- procedure myWin.openInRect (title: string;
- left, top, right, bottom: integer);
-
- { The purpose of this procedure is to open a window, initialize its data structures, etc. }
- { The title is displayed in the title bar of the window; the position and size of the }
- { window are determined by the remaining parameters. (top,left) is the point at the }
- { top, left corner of the inside part of the window, and (right,bottom) is the bottom }
- { right corner. Ordinarily, this procedure is called by procedure Open, instead of }
- { being used directly. }
-
- var
- frame: xRectangle;
- str: xDisplayString; { types xRectangle and xDisplayString is defined in UNIT xFigureDecoration }
- begin
-
- inherited openInRect(title, left, top, right, bottom);
-
- new(graph); { install graph-drawing decoration }
- graph.Setup(self, 200, 10, -10, -40);
- graph.SetCoordinates(-5, 5, -5, 5);
-
- new(frame); { install a box around the graph area }
- frame.Setup(self, 198, 8, -8, -38);
- frame.SetNib(2, 2);
-
- new(str); { install labels for input boxes }
- str.setUp(self, 'xmin =', 10, 60);
- new(str);
- str.setUp(self, 'xmax =', 10, 90);
- new(str);
- str.setUp(self, 'ymin =', 10, 120);
- new(str);
- str.setUp(self, 'ymax =', 10, 150);
- new(str);
- str.setUp(self, 'f(x) =', 5, -25);
-
- new(xminIn); { install input boxes }
- xminIn.SetUp(self, 65, 55, 110, 24);
- xminIn.SetContentsToNumber(-5);
-
- new(xmaxIn);
- xmaxIn.SetUp(self, 65, 85, 110, 24);
- xmaxIn.SetContentsToNumber(5);
-
- new(yminIn);
- yminIn.SetUp(self, 65, 115, 110, 24);
- yminIn.SetContentsToNumber(-5);
-
- new(ymaxIn);
- ymaxIn.SetUp(self, 65, 145, 110, 24);
- ymaxIn.SetContentsToNumber(5);
-
- new(expIn);
- expIn.Setup(self, 50, -30, -25, 24);
-
- new(calc); { install button }
- calc.setup(self, 'Graph It!', 50, 10, 100, 20);
-
- SetMinDragWidth(350);
- SetMinDragHeight(210);
-
- end;
-
-
- procedure myWin.doRedraw (badRect: Rect);
- { This procedure is called when all or part of the stuff in the window needs to be }
- { redrawn. The parameter badRect specifies the region of the window that needs to }
- { be redrawn; the usual thing to do is to just ignore it and to redraw the entire }
- { window. }
- { This procedure is called, for example, when another window that has been }
- { covering this one is moved or closed. By default, it is also called when the user }
- { changes the position of the scroll bar. In both cases, the widow is erased before }
- { the procedure is called. (Erasing and redrawing the window may not be the effect }
- { you want when the window is scrolled; to change this default behaviour, you have }
- { to override the procedures doHScroll and doVScroll.) }
- { The default method in xWindow for doRedraw redraws any }
- { xWindowDecorations in the Window. Some subclasses of xWindow have }
- { other responses to the message doContentClick. Unless you are sure you }
- { have no reason to do so, you sould call INHERITED doContentClick in addition }
- { to any other redrawing you do for your window. }
- begin
-
- inherited doRedraw(badRect); { ordinarily, you should call this somewhere in your procedure }
-
- end;
-
-
- procedure myWin.doContentClick (localPt: Point;
- modifiers: longint);
- { This procedure is called when the user clicks on the "content region" of the }
- { window. (If the user clicks on the scroll bar, title bar, or grow box, that is }
- { handled automatically; this procedure is not called in such cases.) The point }
- { where the user clicked is given by the parameter localPt. (The point is in "local }
- { coordinates," i.e. is specified relative to the top left corner of the window.) }
- { The parameter modifiers can be used to check whether the shift, command or }
- { option keys were held down when the mouse was clicked. This sample program }
- { does not respond to clicks. }
- { The default method in xWindow for doContentClick sends the doClick to any appropriate }
- { xWindowDecoration in the Window. Some subclasses of xWindow have }
- { other responses to the message doContentClick. Unless you are sure you }
- { have no decorations in your window, you sould call INHERITED doContentClick }
- { whenever your procedure does not handle the click itself. }
- begin
- inherited doContentClick(localPt, modifiers);
- end;
-
-
- procedure myWin.doKey (ch: char;
- modifiers: longint);
- { This procedure is called when the user presses a key, provided this is the front }
- { (or "active") window. This procedure is not used by the sample program. }
- { The default method in xWindow for doKey sends a doKey message to any appropriate }
- { xWindowDecoration in the Window. Some subclasses of xWindow have }
- { other responses to the message doKey. Unless you want some other response }
- { to a key, you sould call INHERITED doKey (or simply do not override this method }
- { in your window class.) }
- begin
- inherited doKey(ch, modifiers)
- end;
-
-
- procedure OpenAWindow;
- { A very common procedure is one for opening a new window. In this sample }
- { application, this procedure is called by InitializeApplication to open an initial }
- { window. It is also called when the user chooses the command New from the File }
- { menu. }
- var
- Win: myWin;
- begin
- new(Win);
- Win.open('Sample Window');
- end;
-
-
- { *** Definitions of procedures that are called in the main program. *** }
-
-
- procedure InitializeApplication;
- { Called when the program is first starting up; use this procedure to initialize }
- { any global variables, open any windows that you want to appear automatically }
- { on the screen at startup, etc. }
- begin
-
- initExpressions; { initialize UNIT expressions, and create the variable "x" )}
- xRef := CreateVariable('x', 0);
-
- OpenAWindow;
-
- end;
-
- procedure CleanUpApplication;
- { This is called just before the program ends, to give you a final chance to clean }
- { up. Most programs will have no use for this. There is no way to abort program }
- { termination from this procedure. }
- begin
- end;
-
- procedure UpdateMenus;
- { This procedure is called just BEFORE the user makes a selection from the }
- { menu bar, after the user has clicked in the menu bar, but before any menu is }
- { displayed. Use this procedure to enable and disable items in the menu, to set }
- { the names of items with changeable names, to check and uncheck items, etc. }
- var
- i: integer;
- win: WindowPtr;
- begin
- win := FrontWindow; { this is the currently active window, or nil if there is none }
- if win = nil then
- DisableItem(fileMenu, 2)
- else
- EnableItem(fileMenu, 2);
- end;
-
-
- procedure CloseFrontWindow;
- var
- X: xWindow;
- begin
- if FrontWindow <> nil then
- if window2xWindow(FrontWindow, X) then
- X.doClose
- end;
-
- procedure DoFileMenu (itemNum: integer;
- var done: boolean);
- { Perform command number "itemNum" from the File Menu; if the user has chosen }
- { the Quit command, then the parameter "done" should be set to TRUE, unless }
- { the command is canceled in one way or another (for example, if you put up a }
- { dialog box that says "Do you really want to quit?" and the user answers No). }
- { In this sample program, the only commands in the File menu are New (command }
- { #1), Close (#2) and Quit (#4). }
- begin
- case itemNum of
- 1: { New command }
- OpenAWindow;
- 2: { Close command }
- CloseFrontWindow;
- 4: { Quit command }
- done := true;
- end;
- end;
-
- procedure DoEditMenu (itemNum: integer);
- { The user has chosen the specified item number from the Edit menu. This }
- { procedure should carry out that command. The sample program does not }
- { use these commands. }
- begin
- end;
-
- procedure DoOtherMenu (menuNum, itemNum: integer);
- { This procedure is here just in case you add more menus to the menu bar. The }
- { user has selected item number "itemNum" from menu number "menuNum", }
- { and this procedure should carry out that command. Note that the menu number }
- { used here is the menu ID (or resource number), not the position of the menu in }
- { the menu bar. This sample program has no extra menus. }
- begin
- end;
-
- procedure ApplicationIdle;
- { This procedure is called periodically--at least six times a second, unless the }
- { computer is otherwise occupied (for example, tied up in a lengthy computation). }
- { (This default procedure simply sends an idle message to the front window, }
- { if any.) }
- var
- X: xWindow;
- begin
- if (FrontWindow <> nil) & (window2xWindow(FrontWindow, X)) then
- X.idle;
- end;
-
- end.